package BLC

import (
	"fmt"
	"github.com/boltdb/bolt"
	"log"
	"math/big"
	"time"
)

const (
	//数据库名字
	dbName = "blockchain.db"
	//表名
	blockTableName = "blocks"
)

type Blockchain struct {
	Tip []byte //最新区块的Hash
	DB  *bolt.DB
}

//迭代器
func (Blockchain *Blockchain) Iterator() *BlockchainIterator {
	return &BlockchainIterator{Blockchain.Tip, Blockchain.DB}
}

type BlockchainIterator struct {
	CurrentHash []byte
	DB          *bolt.DB
}

func (blockchainIterator *BlockchainIterator) Next() *Block {

	var block *Block
	err := blockchainIterator.DB.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(blockTableName))
		if b != nil {
			currentBlockBytes := b.Get(blockchainIterator.CurrentHash)
			//获取到当前迭代器 中 currentHash 所对应的区块
			block = DeserializeBlocks(currentBlockBytes)
			//更新迭代器中的 Hash
			blockchainIterator.CurrentHash = block.Hash

		}

		return nil
	})
	if err != nil {
		log.Panic(err)
	}
	return block
}

// 1. 创建带有创世区块的区块链
func CreatBlockchain() *Blockchain {
	db, err := bolt.Open(dbName, 0600, nil)
	if err != nil {
		log.Fatal(err)
	}
	var blockHash []byte
	err = db.Update(func(tx *bolt.Tx) error {
		b, err := tx.CreateBucketIfNotExists([]byte(blockTableName))
		if err != nil {
			log.Panic(err)
		}
		if b == nil {
			genesisBlock := CreateGenesisBlock("Genesis Data....")
			err := b.Put(genesisBlock.Hash, genesisBlock.Serialize())
			if err != nil {
				log.Panic(err)
			}
			// 存储最新的区块的Hash
			err = b.Put([]byte("L"), genesisBlock.Hash)
			if err != nil {
				log.Panic(err)
			}
			blockHash = genesisBlock.Hash
		} else {
			blockHash = b.Get([]byte("L"))
		}
		return nil
	})
	if err != nil {
		log.Panic(err)
	}
	return &Blockchain{blockHash, db}
}

// 2. 增加区块到区块链中
func (blc *Blockchain) AddBlockToBlockchain(data string) {

	err := blc.DB.Update(func(tx *bolt.Tx) error {
		// 获取表
		b := tx.Bucket([]byte(blockTableName))
		if b != nil { //创建新区块
			block := DeserializeBlocks(b.Get(blc.Tip))
			newBlock := NewBlock(data, block.Height+1, block.Hash)
			err := b.Put(newBlock.Hash, newBlock.Serialize())
			if err != nil {
				log.Panic(err)
			}
			// 存储最新的区块的Hash
			err = b.Put([]byte("L"), newBlock.Hash)
			blc.Tip = newBlock.Hash
		}
		return nil
	})
	if err != nil {
		log.Panic(err)
	}
}

// 遍历输出所有区块的信息
func (blc *Blockchain) Printchain() {
	blockchainIterator := blc.Iterator()
	for true {
		block := blockchainIterator.Next()
		var hashInt big.Int
		hashInt.SetBytes(block.PrevBlockHash)
		if big.NewInt(0).Cmp(&hashInt) == 0 {
			break
		}
		fmt.Println()
		fmt.Printf("Height: %d", block.Height)
		fmt.Printf("PrevBlockHash: %x", block.PrevBlockHash)
		fmt.Printf("Data: %s", block.Data)
		fmt.Printf("Timestamp: %s", time.Unix(block.Timestamp, 0).Format("2006-01-02 15:04:05"))
		fmt.Printf("Hash: %x", block.Hash)
		fmt.Printf("Nonce: %d", block.Nonce)

	}

}
